﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AAA.DesignPattern.Observer;
using AAA.TradeLanguage.Data;

namespace AAA.TradeLanguage
{
    public sealed class SignalManager : ISubject
    {
        private Subject _subject;

        // Signal Name, SingalInfo
        private Dictionary<string, SignalInfo> _dicLimitOrder;

        // Signal Name, Stop SignalInfo
        private Dictionary<string, SignalInfo> _dicStopOrder;

        //Signal Name, Market SignalInfo
        private Dictionary<string, SignalInfo> _dicMarketOrder;

        // Price, Position
        private List<PositionInfo> _lstCurrentPosition;

        // Signal Record History
        private List<SignalRecord> _lstSignalRecord;

        private int _iMinEntryPosition;

        public int MinEntryPosition
        {
            get { return _iMinEntryPosition; }
            set { _iMinEntryPosition = value; }
        }

        private int _iMaxPosition;

        public int MaxPosition
        {
            get { return _iMaxPosition; }
            set { _iMaxPosition = value; }
        }

        public List<SignalRecord> TradeRecord()
        {
            return _lstSignalRecord;
        }

        public SignalManager()
        {
            Reset();
            _subject = new Subject();
        }

        public void Reset()
        {
            _dicLimitOrder = new Dictionary<string, SignalInfo>();
            _dicStopOrder = new Dictionary<string, SignalInfo>();
            _dicMarketOrder = new Dictionary<string, SignalInfo>();
            _lstCurrentPosition = new List<PositionInfo>();
            _lstSignalRecord = new List<SignalRecord>();
        }

        private SignalInfo FillSignal(SignalInfo signalInfo, PositionInfo positionEntry, PositionInfo positionExit)
        {
            SignalInfo notifySignalInfo = new SignalInfo();

            notifySignalInfo.PositionId = positionEntry.PositionId;
            notifySignalInfo.SignalGroupName = signalInfo.SignalGroupName;
            notifySignalInfo.SignalName = positionExit.SignalName;// signalInfo.SignalName;
            notifySignalInfo.TimePlaced = positionExit.SignalTime;
            notifySignalInfo.OrderDirection = positionEntry.OrderDirection == OrderDirectionEnum.LongEntry
                                                ? OrderDirectionEnum.LongExit
                                                : OrderDirectionEnum.ShortExit; ;
            notifySignalInfo.OrderType = signalInfo.OrderType;
            notifySignalInfo.Price = positionExit.Price;
            notifySignalInfo.Position = positionExit.Position;
            notifySignalInfo.BaseSymbolId = signalInfo.BaseSymbolId;

            return notifySignalInfo;     
        }

        private SignalRecord FillSignalRecord(PositionInfo positionEntry, PositionInfo positionExit)
        {
            SignalRecord signalRecord = new SignalRecord();

            signalRecord = new SignalRecord();
            signalRecord.EntryDateTime = positionEntry.SignalTime;
            signalRecord.EntryPrice = positionEntry.Price;
            signalRecord.EntryOrderDirection = positionEntry.OrderDirection;
            signalRecord.EntrySignalName = positionEntry.SignalName;

            signalRecord.ExitDateTime = positionExit.SignalTime;
            signalRecord.ExitOrderDirection = positionEntry.OrderDirection == OrderDirectionEnum.LongEntry
                                                ? OrderDirectionEnum.LongExit 
                                                : OrderDirectionEnum.ShortExit;
                                               
            signalRecord.ExitPrice = positionExit.Price;
            signalRecord.ExitSignalName = positionExit.SignalName;

            return signalRecord;
        }

        public List<PositionInfo> GetCurrentPosition()
        {
            return _lstCurrentPosition;
        }

        public void MatchPrice(float fMatchPrice)
        {
            List<SignalInfo> lstSignalInfo;
            PositionInfo positionInfo = null;
            SignalRecord signalRecord = null;
            try
            {
                foreach (SignalInfo signalInfo in _dicMarketOrder.Values)
                {
/*                    
                    if (signalInfo.Price != fMatchPrice)
                        continue;
*/
                    positionInfo = new PositionInfo();
                    positionInfo.PositionId = DateTime.Now.Ticks.ToString();
                    positionInfo.OrderDirection = signalInfo.OrderDirection;
                    positionInfo.Price = fMatchPrice;
                    positionInfo.SignalName = signalInfo.SignalName;
                    positionInfo.SignalTime = signalInfo.TimePlaced;

                    switch (signalInfo.OrderDirection)
                    {
                        case OrderDirectionEnum.LongEntry:
                            for (int i = _lstCurrentPosition.Count - 1; i >= 0; i--)
                            {
                                if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.ShortEntry)
                                {
                                    signalRecord = FillSignalRecord(_lstCurrentPosition[i], positionInfo);
                                    NotifySignal(FillSignal(signalInfo, _lstCurrentPosition[i], positionInfo), "Filled");
                                    _lstSignalRecord.Add(signalRecord);
                                    _lstCurrentPosition.RemoveAt(i);
                                }
                            }                            
                            positionInfo.Position = signalInfo.Position;
                            if (_lstCurrentPosition.Count < MaxPosition)
                            {
                                _lstCurrentPosition.Add(positionInfo);
                                signalInfo.PositionId = positionInfo.PositionId;
                                NotifySignal(signalInfo, "Filled");
                            }
                            break;
                        case OrderDirectionEnum.ShortEntry:
                            for (int i = _lstCurrentPosition.Count - 1; i >= 0; i--)
                            {
                                if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.LongEntry)
                                {
                                    signalRecord = FillSignalRecord(_lstCurrentPosition[i], positionInfo);                                    
                                    NotifySignal(FillSignal(signalInfo, _lstCurrentPosition[i], positionInfo), "Filled");
                                    _lstSignalRecord.Add(signalRecord);
                                    _lstCurrentPosition.RemoveAt(i);
                                }
                            }

                            positionInfo.Position = -1 * signalInfo.Position;
                            if (_lstCurrentPosition.Count < MaxPosition)
                            {
                                _lstCurrentPosition.Add(positionInfo);
                                signalInfo.PositionId = positionInfo.PositionId;
                                NotifySignal(signalInfo, "Filled");
                            }
                            break;
                        case OrderDirectionEnum.LongExit:
                            for (int i = 0; i < _lstCurrentPosition.Count; i++)
                                if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.LongEntry)
                                {
                                    signalRecord = FillSignalRecord(_lstCurrentPosition[i], positionInfo);
                                    signalInfo.PositionId = _lstCurrentPosition[i].PositionId;
                                    NotifySignal(signalInfo, "Filled");
                                    _lstSignalRecord.Add(signalRecord);
                                    _lstCurrentPosition.RemoveAt(i);
                                    break;
                                }
                            break;
                        case OrderDirectionEnum.ShortExit:
                            for (int i = 0; i < _lstCurrentPosition.Count; i++)
                                if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.ShortEntry)
                                {
                                    signalRecord = FillSignalRecord(_lstCurrentPosition[i], positionInfo);
                                    signalInfo.PositionId = _lstCurrentPosition[i].PositionId;
                                    NotifySignal(signalInfo, "Filled");                                    
                                    _lstSignalRecord.Add(signalRecord);
                                    _lstCurrentPosition.RemoveAt(i);
                                    break;
                                }
                            break;

                    }

                    _dicMarketOrder = new Dictionary<string, SignalInfo>();                    
                }


                foreach (SignalInfo signalInfo in _dicStopOrder.Values)
                {
                    if (((signalInfo.Price < fMatchPrice) &&
                         (signalInfo.OrderDirection == OrderDirectionEnum.LongExit ||
                          signalInfo.OrderDirection == OrderDirectionEnum.ShortEntry)) ||
                        ((signalInfo.Price > fMatchPrice) &&
                         (signalInfo.OrderDirection == OrderDirectionEnum.LongEntry ||
                          signalInfo.OrderDirection == OrderDirectionEnum.ShortExit)))
                        continue;

                    positionInfo = new PositionInfo();
                    positionInfo.OrderDirection = signalInfo.OrderDirection;
                    positionInfo.Price = fMatchPrice;
                    positionInfo.SignalName = signalInfo.SignalName;
                    positionInfo.SignalTime = signalInfo.TimePlaced;


                    switch (signalInfo.OrderDirection)
                    {
                        case OrderDirectionEnum.LongEntry:
                            for (int i = _lstCurrentPosition.Count - 1; i >= 0; i--)
                            {
                                if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.ShortEntry)
                                {
                                    signalRecord = FillSignalRecord(_lstCurrentPosition[i], positionInfo);
                                    NotifySignal(FillSignal(signalInfo, _lstCurrentPosition[i], positionInfo), "Filled");
                                    _lstSignalRecord.Add(signalRecord);
                                    _lstCurrentPosition.RemoveAt(i);
                                }
                            }

                            positionInfo.Position = signalInfo.Position;
                            if (_lstCurrentPosition.Count < MaxPosition)
                            {
                                _lstCurrentPosition.Add(positionInfo);
                                signalInfo.PositionId = positionInfo.PositionId;
                                NotifySignal(signalInfo, "Filled");
                            }
                            break;
                        case OrderDirectionEnum.ShortEntry:
                            for (int i = _lstCurrentPosition.Count - 1; i >= 0; i--)
                            {
                                if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.LongEntry)
                                {
                                    signalRecord = FillSignalRecord(_lstCurrentPosition[i], positionInfo);
                                    NotifySignal(FillSignal(signalInfo, _lstCurrentPosition[i], positionInfo), "Filled");
                                    _lstSignalRecord.Add(signalRecord);
                                    _lstCurrentPosition.RemoveAt(i);
                                }
                            }

                            positionInfo.Position = -1 * signalInfo.Position;
                            if (_lstCurrentPosition.Count < MaxPosition)
                            {
                                _lstCurrentPosition.Add(positionInfo);
                                signalInfo.PositionId = positionInfo.PositionId;
                                NotifySignal(signalInfo, "Filled");
                            }
                            break;
                        case OrderDirectionEnum.LongExit:
                            for (int i = 0; i < _lstCurrentPosition.Count; i++)
                                if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.LongEntry)
                                {
                                    signalRecord = FillSignalRecord(_lstCurrentPosition[i], positionInfo);
                                    signalInfo.PositionId = _lstCurrentPosition[i].PositionId;
                                    _lstSignalRecord.Add(signalRecord);
                                    _lstCurrentPosition.RemoveAt(i);
                                    break;
                                }
                            break;
                        case OrderDirectionEnum.ShortExit:
                            for (int i = 0; i < _lstCurrentPosition.Count; i++)
                                if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.ShortEntry)
                                {
                                    signalRecord = FillSignalRecord(_lstCurrentPosition[i], positionInfo);
                                    signalInfo.PositionId = _lstCurrentPosition[i].PositionId;
                                    _lstSignalRecord.Add(signalRecord);
                                    _lstCurrentPosition.RemoveAt(i);
                                    break;
                                }
                            break;

                    }

                    _dicStopOrder = new Dictionary<string, SignalInfo>();
                        
                }

                // Process Limit Order;
                foreach (SignalInfo signalInfo in _dicLimitOrder.Values)
                {
                    if (((signalInfo.Price > fMatchPrice) && 
                         (signalInfo.OrderDirection == OrderDirectionEnum.LongExit || 
                          signalInfo.OrderDirection == OrderDirectionEnum.ShortEntry)) ||
                        ((signalInfo.Price < fMatchPrice) && 
                         (signalInfo.OrderDirection == OrderDirectionEnum.LongEntry || 
                          signalInfo.OrderDirection == OrderDirectionEnum.ShortExit)))
                        continue;

                    positionInfo = new PositionInfo();
                    positionInfo.OrderDirection = signalInfo.OrderDirection;
                    positionInfo.Price = fMatchPrice;
                    positionInfo.SignalName = signalInfo.SignalName;
                    positionInfo.SignalTime = signalInfo.TimePlaced;


                    switch (signalInfo.OrderDirection)
                    {
                        case OrderDirectionEnum.LongEntry:
                            for (int i = _lstCurrentPosition.Count - 1; i >= 0; i--)
                            {
                                if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.ShortEntry)
                                {
                                    signalRecord = FillSignalRecord(_lstCurrentPosition[i], positionInfo);
                                    NotifySignal(FillSignal(signalInfo, _lstCurrentPosition[i], positionInfo), "Filled");
                                    _lstSignalRecord.Add(signalRecord);
                                    _lstCurrentPosition.RemoveAt(i);
                                }
                            }

                            positionInfo.Position = signalInfo.Position;
                            if (_lstCurrentPosition.Count < MaxPosition)
                            {
                                _lstCurrentPosition.Add(positionInfo);
                                signalInfo.PositionId = positionInfo.PositionId;
                                NotifySignal(signalInfo, "Filled");
                            }
                            break;
                        case OrderDirectionEnum.ShortEntry:
                            for (int i = _lstCurrentPosition.Count - 1; i >= 0; i--)
                            {
                                if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.LongEntry)
                                {
                                    signalRecord = FillSignalRecord(_lstCurrentPosition[i], positionInfo);
                                    NotifySignal(FillSignal(signalInfo, _lstCurrentPosition[i], positionInfo), "Filled");
                                    _lstSignalRecord.Add(signalRecord);
                                    _lstCurrentPosition.RemoveAt(i);
                                }
                            }

                            positionInfo.Position = -1 * signalInfo.Position;
                            if (_lstCurrentPosition.Count < MaxPosition)
                            {
                                _lstCurrentPosition.Add(positionInfo);
                                signalInfo.PositionId = positionInfo.PositionId;
                                NotifySignal(signalInfo, "Filled");
                            }
                            break;
                        case OrderDirectionEnum.LongExit:
                            for (int i = 0; i < _lstCurrentPosition.Count; i++)
                                if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.LongEntry)
                                {
                                    signalRecord = FillSignalRecord(_lstCurrentPosition[i], positionInfo);
                                    signalInfo.PositionId = _lstCurrentPosition[i].PositionId;
                                    _lstSignalRecord.Add(signalRecord);
                                    _lstCurrentPosition.RemoveAt(i);
                                    break;
                                }
                            break;
                        case OrderDirectionEnum.ShortExit:
                            for (int i = 0; i < _lstCurrentPosition.Count; i++)
                                if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.ShortEntry)
                                {
                                    signalRecord = FillSignalRecord(_lstCurrentPosition[i], positionInfo);
                                    signalInfo.PositionId = _lstCurrentPosition[i].PositionId;
                                    _lstSignalRecord.Add(signalRecord);
                                    _lstCurrentPosition.RemoveAt(i);
                                    break;
                                }
                            break;

                    }

                }
/*
                foreach (float fOrderPrice in _dicLimitOrder.Keys)
                {
                    lstSignalInfo = _dicLimitOrder[fOrderPrice];

                    foreach (SignalInfo signalInfo in lstSignalInfo)
                    {
                        if (((fOrderPrice > fMatchPrice) && (signalInfo.OrderDirection == OrderDirectionEnum.LongExit || signalInfo.OrderDirection == OrderDirectionEnum.ShortEntry)) ||
                           ((fOrderPrice < fMatchPrice) && (signalInfo.OrderDirection == OrderDirectionEnum.LongEntry || signalInfo.OrderDirection == OrderDirectionEnum.ShortExit)))
                            continue;

                        positionInfo = new PositionInfo();
                        positionInfo.OrderDirection = signalInfo.OrderDirection;
                        positionInfo.Price = fMatchPrice;
                        positionInfo.SignalName = signalInfo.SignalName;
                        positionInfo.SignalTime = signalInfo.TimePlaced;

                        switch (signalInfo.OrderDirection)
                        {
                            case OrderDirectionEnum.LongEntry:
                                for (int i = _lstCurrentPosition.Count - 1; i >= 0; i--)
                                {
                                    if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.ShortEntry)
                                    {
                                        signalRecord = FillSignal(_lstCurrentPosition[i], positionInfo);
                                        _lstSignalRecord.Add(signalRecord);
                                    }
                                }

                                positionInfo.Position = signalInfo.Position;
                                _lstCurrentPosition.Add(positionInfo);
                                break;
                            case OrderDirectionEnum.ShortEntry:
                                for (int i = _lstCurrentPosition.Count - 1; i >= 0; i--)
                                {
                                    if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.LongEntry)
                                    {
                                        signalRecord = FillSignal(_lstCurrentPosition[i], positionInfo);
                                        _lstSignalRecord.Add(signalRecord);
                                    }
                                }

                                positionInfo.Position = -1 * signalInfo.Position;
                                _lstCurrentPosition.Add(positionInfo);
                                break;
                            case OrderDirectionEnum.LongExit:
                                for (int i = 0; i < _lstCurrentPosition.Count; i++)
                                    if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.LongEntry)
                                    {
                                        signalRecord = FillSignal(_lstCurrentPosition[i], positionInfo);
                                        _lstSignalRecord.Add(signalRecord);
                                        _lstCurrentPosition.RemoveAt(i);
                                        break;
                                    }
                                break;
                            case OrderDirectionEnum.ShortExit:
                                for (int i = 0; i < _lstCurrentPosition.Count; i++)
                                    if (_lstCurrentPosition[i].OrderDirection == OrderDirectionEnum.ShortEntry)
                                    {
                                        signalRecord = FillSignal(_lstCurrentPosition[i], positionInfo);
                                        _lstSignalRecord.Add(signalRecord);
                                        _lstCurrentPosition.RemoveAt(i);
                                        break;
                                    }
                                break;

                        }

                    }

                }
*/

            }
            catch (Exception ex)
            {

            }
        }

        private void NotifySignal(SignalInfo signalInfo, string strSubject)
        {
            MessageInfo miMessage;
            miMessage = new MessageInfo();
            miMessage.MessageTicks = DateTime.Now.Ticks;
            miMessage.Message = signalInfo;
            miMessage.MessageSubject = strSubject;
            Notify(miMessage);
        }


        public void AddActiveSignal(SignalInfo signalInfo)
        {
            Dictionary<string, SignalInfo> dicOrder = null;

            switch (signalInfo.OrderType)
            {
                case OrderTypeEnum.LimitOrder:
                    dicOrder = _dicLimitOrder;
                    break;
                case OrderTypeEnum.StopOrder:
                    dicOrder = _dicStopOrder;
                    break;

                case OrderTypeEnum.MarketOrder:
                    dicOrder = _dicMarketOrder;
                    break;
            }


            if (dicOrder.ContainsKey(signalInfo.SignalName))
            {
                dicOrder[signalInfo.SignalName] = signalInfo;
            }
            else
            {
                dicOrder.Add(signalInfo.SignalName, signalInfo);
            }
            NotifySignal(signalInfo, "Active");
        }

        public void CancelOrder(string strSignalName)
        {
            if (_dicLimitOrder.ContainsKey(strSignalName))
                _dicLimitOrder.Remove(strSignalName);

            if (_dicStopOrder.ContainsKey(strSignalName))
                _dicStopOrder.Remove(strSignalName);

            if (_dicMarketOrder.ContainsKey(strSignalName))
                _dicMarketOrder.Remove(strSignalName);
        }


        #region ISubject Members

        public void Attach(IObserver observer)
        {
            _subject.Attach(observer);
        }

        public void Detach(IObserver observer)
        {
            _subject.Detach(observer);
        }

        public void Notify(IMessageInfo miMessage)
        {
            _subject.Notify(miMessage);
        }

        #endregion
    }
}
